package com.agileplex.products.acc;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.IOException;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.agileplex.products.acc.commands.AbstractCommand;
import com.agileplex.products.acc.commands.Command;
import com.agileplex.products.acc.commands.CommandType;
import com.agileplex.products.acc.commands.InvokeMethodCommand;
import com.agileplex.products.acc.commands.OpenSessionCommand;
import com.agileplex.products.acc.commands.Parameters;
import com.agileplex.products.acc.generators.IGenerator;

/**
 * Console configuration
 *
 * @creator Rui Tang
 * @date 2008-06-08
 *
 * @LastChanged
 * @LastChangedBy $LastChangedBy: tangrui@agileplex.com $
 * @LastChangedDate $LastChangedDate: 2008-06-22 16:08:49 +0000 (Sun, 22 Jun 2008) $
 * @LastChangedRevision $LastChangedRevision: 7 $
 */
public class Configuration {

    private String version;
    private String prompt;
    private List<Command> commands = new ArrayList<Command>();

    public Configuration() {
        this.version = Constants.DEFAULT_CONFIGURATION_VERSION;
        this.prompt = Constants.DEFAULT_PROMPT;
    }

    public Configuration(File configFile) throws ConfigurationException {
        try {
            InputStream is = new FileInputStream(configFile);
            this.config(is);
        } catch (IOException e) {
            throw new ConfigurationException(e);
        }
    }

    public Configuration(InputStream is) throws ConfigurationException {
        this.config(is);
    }

    @SuppressWarnings("unchecked")
    public void config(InputStream is) throws ConfigurationException {
        try {
            SAXReader reader = new SAXReader();
            reader.setEncoding("UTF-8");

            Document doc = reader.read(is);
            Element rootEl = doc.getRootElement();
            this.version = rootEl.attributeValue("version");
            if (this.version == null) {
                this.version = Constants.DEFAULT_CONFIGURATION_VERSION;
            }

            for (Iterator<Element> it = rootEl.elementIterator(); it.hasNext(); ) {
                Element el = it.next();
                String elName = el.getName();

                if ("prompt".equals(elName)) {
                    this.prompt = el.getText();
                    if (this.prompt == null) {
                        this.prompt = Constants.DEFAULT_PROMPT;
                    }
                } else if ("command".equals(elName)) {
                    Command cmd = this.parseCommand(el);
                    this.commands.add(cmd);
                }
            }
        } catch (DocumentException e) {
            throw new ConfigurationException(e);
        } catch (IOException e) {
            throw new ConfigurationException(e);
        }
    }

    public String getPrompt() {
        return this.prompt;
    }

    public List<Command> getCommands() {
        return this.commands;
    }

    public Session createSession() {
        return new Session(this);
    }

    @SuppressWarnings("unchecked")
    private Command parseCommand(Element commandEl) throws ConfigurationException, IOException {
        String commandName = commandEl.attributeValue("name");
        String commandType = commandEl.attributeValue("type");

        Command cmd = null;
        if ("open-session".equals(commandType)) {
            OpenSessionCommand osCmd = new OpenSessionCommand(commandName);
            osCmd.setType(CommandType.OPEN_SESSION);
            osCmd.setConfigurationFileName(commandEl.attributeValue("configuration"));
            cmd = osCmd;
        } else if ("invoke-method".equals(commandType)) {
            InvokeMethodCommand imCmd = new InvokeMethodCommand(commandName);
            imCmd.setType(CommandType.INVOKE_METHOD);
            imCmd.setClassName(commandEl.attributeValue("class"));
            imCmd.setMethodName(commandEl.attributeValue("method"));
            cmd = imCmd;
        } else {
            AbstractCommand absCmd = new AbstractCommand(commandName);
            for (Iterator<Element> it = commandEl.elementIterator(); it.hasNext(); ) {
                Element subCommandEl = it.next();
                Command subCmd = this.parseCommand(subCommandEl);
                absCmd.addCommand(subCmd);
            }
            cmd = absCmd;
        }
        try {
            cmd.setParameters(this.parseParameters(commandEl));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return cmd;
    }

    @SuppressWarnings("unchecked")
    private Parameters parseParameters(Element commandEl) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Element parametersEl = null;
        for (Iterator<Element> it = commandEl.elementIterator(); it.hasNext(); ) {
            Element subEl = it.next();
            if ("parameters".equals(subEl.getName())) {
                parametersEl = subEl;
                break;
            }
        }

        if (parametersEl != null) {
            String generator = parametersEl.attributeValue("generator");
            if (generator == null) {
                generator = Constants.DEFAULT_GENERATOR_QUALIFIED_NAME;
            }
            Class<IGenerator> clazz = (Class<IGenerator>)Class.forName(generator);
            IGenerator gen = clazz.newInstance();
            return gen.generate(parametersEl);
        }

        return null;
    }
}
